#!/usr/bin/python2

# Python Imports
import sys
sys.dont_write_bytecode = True

# Library Imports
import pygame

# Project Imports
from utils import Constants as Const, Patterns
import maps
from sprites.spikes import Spike


def main():
    # Initialize PyGame modules
    pygame.init()
    pygame.font.init()
    pygame.mixer.init()
    # Initialize Display
    pygame.display.set_caption("Rotate AD")
    pygame.display.set_icon(Spike(0, 0).image)
    screen = pygame.display.set_mode((Const.SIZE, Const.SIZE))
    buff = pygame.surface.Surface((Const.SIZE, Const.SIZE))
    # Initialize Fonts
    font1 = pygame.font.SysFont("Arial", 72)
    font2 = pygame.font.SysFont("Arial", 20)
    # Load Sounds
    pygame.mixer.music.load("menu.ogg")
    pygame.mixer.music.play(-1)
    # Load Map
    cmap = maps.menu.Menu
    player, groups = cmap.load_map()
    # Initialize Game Elements
    lives = Const.LIVES
    respawn = 0
    rotation = 0
    # Game Loop
    clock = pygame.time.Clock()
    state = Const.STATE_MENU
    while True:
        ps = state
        delta = clock.tick(Const.FPS)
        # Handle events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit(0)
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_a:
                    rotation = (rotation + 90) % 360
                elif event.key == pygame.K_d:
                    rotation = (rotation + 270) % 360
        if state == Const.STATE_DEAD:
            rotation = 0
            # Clear the surface
            buff.fill(pygame.color.Color(Const.DEAD_COLOR))
            # Paint Respawn
            respawn -= delta
            text = "Respawn in... %0.1f seconds" % (respawn / 1000.0)
            tw, th = font2.size(text)
            rin = font2.render(text, False,
                               pygame.color.Color(Const.FONT_COLOR),
                               pygame.color.Color(Const.DEAD_COLOR))
            buff.blit(rin, (Const.SIZE // 2 - tw // 2,
                            Const.SIZE * 3 // 4 - th // 2))
            # Paint lives left or game over
            text = "Game Over" if lives == 0 else ("%s Lives Left" % lives)
            tw, th = font1.size(text)
            left = font1.render(text, False,
                                pygame.color.Color(Const.FONT_COLOR),
                                pygame.color.Color(Const.DEAD_COLOR))
            buff.blit(left, (Const.SIZE // 2 - tw // 2,
                             Const.SIZE * 3 // 8 - th // 2))
            # Is time to respawn, respawn
            if respawn <= 0:
                # Check if game over, if so restart
                if lives == 0:
                    cmap = maps.menu.Menu
                    player, groups = cmap.load_map()
                    rotation = 0
                    state = Const.STATE_MENU
                else:
                    # Reset level
                    player, groups = cmap.load_map()
                    rotation = 0
                    state = Const.STATE_GAME
        elif state == Const.STATE_FINISH:
            rotation = 0
            # Clear the surface
            buff.fill(pygame.color.Color(Const.FINISH_COLOR))
            respawn -= delta
            if cmap:
                text = "Next level in... %0.1f seconds" % (respawn / 1000.0)
                tw, th = font2.size(text)
                rin = font2.render(text, False,
                                   pygame.color.Color(Const.FONT_COLOR),
                                   pygame.color.Color(Const.FINISH_COLOR))
                buff.blit(rin, (Const.SIZE // 2 - tw // 2,
                                Const.SIZE * 3 // 4 - th // 2))
            # Paint next level in
            text = "You Won!" if not cmap else "Good Job!"
            tw, th = font1.size(text)
            left = font1.render(text, False,
                                pygame.color.Color(Const.FONT_COLOR),
                                pygame.color.Color(Const.FINISH_COLOR))
            buff.blit(left, (Const.SIZE // 2 - tw // 2,
                             Const.SIZE * 3 // 8 - th // 2))
            if respawn <= 0:
                if cmap:
                    player, groups = cmap.load_map()
                    rotation = 0
                    state = Const.STATE_GAME
                else:
                    cmap = maps.menu.Menu
                    player, groups = cmap.load_map()
                    rotation = 0
                    state = Const.STATE_MENU
        elif state == Const.STATE_GAME:
            # Clear the surface
            buff.fill(pygame.color.Color(Const.BACKGROUND_COLOR))
            mgrps = list(groups[k] for k in groups if not k.startswith("_"))
            # Update the sprites
            for group in [player] + mgrps:
                group.update(delta / 1000.0, rotation, groups)
            # Paint the sprites
            for group in mgrps + [player]:
                group.draw(buff)
            # Check if dead
            if player.dead:
                lives -= 1
                state = Const.STATE_DEAD
                respawn = Const.RESPAWN_TIME
                rotation = 0
            # Check if finished
            if not len(groups["finish"].sprites()):
                state = Const.STATE_FINISH
                respawn = Const.RESPAWN_TIME
                rotation = 0
                nmap = "map%s" % (eval(cmap.__name__.replace("Map", "")) + 1)
                if nmap in maps.__dict__:
                    if nmap.title() in maps.__dict__[nmap].__dict__:
                        cmap = maps.__dict__[nmap].__dict__[nmap.title()]
                    else:
                        cmap = None
                else:
                    cmap = None
        elif state == Const.STATE_MENU:
            # Clear the surface
            buff.fill(pygame.color.Color(Const.BACKGROUND_COLOR))
            mgrps = list(groups[k] for k in groups if not k.startswith("_"))
            # Update the sprites
            for group in [player] + mgrps:
                group.update(delta / 1000.0, rotation, groups)
            # Paint the sprites
            for group in [player] + mgrps:
                group.draw(buff)
            # Paint the title
            text = "Rotate AD"
            tw, th = font1.size(text)
            t = font1.render(text, False,
                             pygame.color.Color(Const.FONT_COLOR2))
            _, ty = groups["__menu_title"]
            buff.blit(t, (Const.SIZE // 2 - tw // 2, ty + th // 8))
            # Paint the the objective
            tx, ty = groups["__menu_objective"]
            tx += 10
            ty += 10
            text = "Objectives"
            tw, th = font2.size(text)
            t = font2.render(text, False,
                             pygame.color.Color(Const.FONT_COLOR2))
            buff.blit(t, (Const.SIZE // 2 - tw // 2, ty))
            for line in ("- Move the smaller green square to the larger one",
                         "- Unlock doors by getting the key with the same #",
                         "- Avoid hitting spikes"):
                ty += th + 2
                tw, th = font2.size(line)
                t = font2.render(line, False,
                                 pygame.color.Color(Const.FONT_COLOR2))
                buff.blit(t, (tx, ty))
            # Paint the controls
            tx, ty = groups["__menu_controls"]
            tx += 10
            ty += 10
            text = "Controls"
            tw, th = font2.size(text)
            t = font2.render(text, False,
                             pygame.color.Color(Const.FONT_COLOR2))
            buff.blit(t, (Const.SIZE // 2 - tw // 2, ty))
            for line in ("A - Rotate 90 degrees counter-clockwise",
                         "D - Rotate 90 degrees clockwise",
                         "(Press A/D to move the square below to make a "
                         "selection)"):
                ty += th + 2
                tw, th = font2.size(line)
                t = font2.render(line, False,
                                 pygame.color.Color(Const.FONT_COLOR2))
                buff.blit(t, (tx, ty))
            # Paint the the credit line
            tx, ty = groups["__menu_credits"]
            text = "%-25s%25s" % ("RedSoxFan/GanjaGoomy", "Mini Ludum Dare 56")
            tw, th = font2.size(text)
            t = font2.render(text, False,
                             pygame.color.Color(Const.FONT_COLOR2))
            buff.blit(t, (Const.SIZE // 2 - tw // 2, ty + th // 8))
            # Check to see if an action has been selected
            for x in (v for k, v in groups.iteritems()
                      if k.startswith("_menu_")):
                if not x.alive():
                    z = x.which()
                    if isinstance(z, str):
                        if z == "mute":
                            pygame.mixer.music.set_volume(0)
                        elif z == "unmute":
                            pygame.mixer.music.set_volume(1)
                    else:
                        cmap = z
                        if cmap and cmap.get_map_name() == "Level Selection":
                            player, groups = cmap.load_map()
                            state = Const.STATE_SELECT
                            rotation = 0
                        elif cmap:
                            player, groups = cmap.load_map()
                            state = Const.STATE_GAME
                            rotation = 0
                        else:
                            sys.exit(0)
        elif state == Const.STATE_SELECT:
            # Clear the surface
            buff.fill(pygame.color.Color(Const.BACKGROUND_COLOR))
            mgrps = list(groups[k] for k in groups if not k.startswith("_"))
            # Update the sprites
            for group in [player] + mgrps:
                group.update(delta / 1000.0, rotation, groups)
            # Paint the sprites
            for group in [player] + mgrps:
                group.draw(buff)
            # Check to see if an action has been selected
            for x in (v for k, v in groups.iteritems()
                      if k.startswith("_menu_")):
                if not x.alive():
                    z = x.which()
                    if not isinstance(z, str):
                        cmap = z
                        if cmap and cmap.get_map_name() == "Level Selection":
                            player, groups = cmap.load_map()
                            state = Const.STATE_SELECT
                            rotation = 0
                        elif cmap and cmap.get_map_name() == "Main Menu":
                            player, groups = cmap.load_map()
                            state = Const.STATE_MENU
                            rotation = 0
                        elif cmap:
                            player, groups = cmap.load_map()
                            state = Const.STATE_GAME
                            rotation = 0
                        else:
                            sys.exit(0)
        # Rotate the buffer
        rb = pygame.transform.rotate(buff, rotation)
        # Paint HUD
        if state == Const.STATE_GAME:
            text = "%s: %s" % (cmap.__name__.replace("Map", "Level "),
                               cmap.get_map_name())
            tw, th = font2.size(text)
            lev = font2.render(text, False,
                               pygame.color.Color(Const.FONT_COLOR2))
            rb.blit(lev, (10, (Const.GRID_SIZE - th) // 2))
            livcol = pygame.color.Color(Const.LIVES_COLOR)
            for n in xrange(lives):
                for r in xrange(min(len(Patterns.LIVES), Const.GRID_SIZE)):
                    for c in xrange(min(len(Patterns.LIVES[r]),
                                        Const.GRID_SIZE)):
                        if Patterns.LIVES[r][c].strip():
                            x = Const.SIZE - (Const.LIVES_SIZE + 5) * n
                            y = (Const.GRID_SIZE - Const.LIVES_SIZE) // 2
                            pygame.draw.rect(rb, livcol, (x + c, y + r, 2, 2))
        # Blit rotated buffer to screen
        screen.blit(rb, (0, 0))
        # Flip the screen
        pygame.display.flip()
        # Check audio
        menus = (Const.STATE_MENU, Const.STATE_SELECT)
        if state in menus and ps not in menus:
            pygame.mixer.music.load("menu.ogg")
            pygame.mixer.music.play(-1)
        elif state not in menus and ps in menus:
            pygame.mixer.music.load("SawSymphony_Dmin.ogg")
            pygame.mixer.music.play(-1)

if __name__ == "__main__":
    main()
